﻿using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Windows;
using System.Windows.Input;
using Language.Syntax.Colouring;
using Language.Utility;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Operations;
using Microsoft.VisualStudio.Utilities;

namespace Language.Intellisense.Goto
{
    internal class Mouse : MouseProcessorBase
    {
        #region Private Data

        private Span? _activeSpan;

        #endregion Private Data

        #region Public Properties

        public IWpfTextView TextView { get; private set; }
        public ITextStructureNavigator Navigator { get; private set; }
        public DynamicClassificationProvider DynamicClassificationProvider { get; private set; }
        public Stub.Database Database { get; private set; }

        public Span? ActiveSpan
        {
            get { return _activeSpan; }
            private set
            {
                if( _activeSpan != value )
                {
                    if( _activeSpan != null )
                    {
                        DynamicClassificationProvider[ TextView ][ _activeSpan.Value ] = null;
                        System.Windows.Input.Mouse.OverrideCursor = null;
                    }

                    _activeSpan = value;

                    if ( _activeSpan != null )
                    {
                        DynamicClassificationProvider[ TextView ][ _activeSpan.Value ] = DynamicClassificationProvider.ActionGotoDefClassification;
                        System.Windows.Input.Mouse.OverrideCursor = Cursors.Hand;
                    }
                }
            }
        }

        #endregion Public Properties

        #region C-Tor

        public Mouse( IWpfTextView wpfTextView, ITextStructureNavigator navigatior, DynamicClassificationProvider dynamicClassificationProvider, Stub.Database database )
        {
            TextView = wpfTextView;
            Navigator = navigatior;
            DynamicClassificationProvider = dynamicClassificationProvider;
            Database = database;

            TextView.Closed += TextView_Closed;
        }

        #endregion C-Tor

        #region Private Methods

        private Point RelativeToView( Point position )
        {
            return new Point( position.X + TextView.ViewportLeft, position.Y + TextView.ViewportTop );
        }

        private bool GetBufferPositionFromMouse( Point position, out SnapshotPoint bufferPosition )
        {
            bufferPosition = default( SnapshotPoint );

            var line = TextView.TextViewLines.GetTextViewLineContainingYCoordinate( position.Y );

            if ( line == null )
                return false;

            SnapshotPoint? bufferPositionUnresolved = line.GetBufferPositionFromXCoordinate( position.X );

            if ( !bufferPositionUnresolved.HasValue )
                return false;

            bufferPosition = bufferPositionUnresolved.Value;
            return true;
        }

        private Span? ValidWordSpanUnderMouse( Point position )
        {
            Point relativePosition = RelativeToView( position );

            SnapshotPoint bufferPosition;
            if ( !GetBufferPositionFromMouse( relativePosition, out bufferPosition ) )
                return null;

            if ( ActiveSpan?.Contains( bufferPosition ) == true )
                return ActiveSpan;

            TextExtent wordExtents = Navigator.GetExtentOfWord( bufferPosition );
            if ( !wordExtents.IsSignificant )
                return null;

            string textUnderCursor = TextView.TextSnapshot.GetText( wordExtents.Span );

            string path = TextView.TextBuffer.ExtractFilename();

            Stub.DetailedContext context = Database.FindDeclaration( path, bufferPosition.Position );
            if ( context == null )
                return null;

            return wordExtents.Span;
        }

        #endregion Private Methods

        #region Event Handlers

        private void TextView_Closed( object sender, System.EventArgs e )
        {
            TextView.Closed -= TextView_Closed;
        }

        #endregion Event Handlers

        #region MouseProcessorBase

        public override void PostprocessMouseLeftButtonUp( MouseButtonEventArgs e )
        {
            if ( ActiveSpan.HasValue && ( Keyboard.Modifiers & ModifierKeys.Control ) == ModifierKeys.Control )
            {
                string path = TextView.TextBuffer.ExtractFilename();

                Stub.DetailedContext context = Database.FindDeclaration( path, ActiveSpan.Value.Start );

                if ( context != null )
                {
                    Debug.Print( $"Goto: {context.File}({context.Line}) {context.Position}" );

                    Utility.Goto.Symbol( context );
                }
            }

            ActiveSpan = null;
        }

        public override void PostprocessMouseMove( MouseEventArgs e )
        {
            if ( ( Keyboard.Modifiers & ModifierKeys.Control ) == ModifierKeys.Control )
            {
                ActiveSpan = ValidWordSpanUnderMouse( e.GetPosition( TextView.VisualElement ) );
            }
            else
            {
                ActiveSpan = null;
            }
        }

        #endregion MouseProcessorBase
    }

    #region Provider

    [Export( typeof( IMouseProcessorProvider ) )]
    [Name( "Mouse Processor Provider" )]
    [ContentType( "redscript" )]
    [TextViewRole( PredefinedTextViewRoles.Interactive )]
    internal sealed class MouseProcessorProvider : IMouseProcessorProvider
    {
        #region Private Properties

        [Import]
        private ITextStructureNavigatorSelectorService NavigatorService { get; set; }
        [Import]
        private DynamicClassificationProvider DynamicClassificationProvider { get; set; }
        [Import]
        private Stub.Database Database { get; set; }

        #endregion Private Properties

        #region IMouseProcessorProvider

        public IMouseProcessor GetAssociatedProcessor( IWpfTextView wpfTextView )
        {
            return new Mouse( wpfTextView, NavigatorService.GetTextStructureNavigator( wpfTextView.TextBuffer ), DynamicClassificationProvider, Database );
        }

        #endregion IMouseProcessorProvider
    }

    #endregion Provider
}
